<template>
    <div id="app" class="allthing" v-if="!isLoading">
        <div id="word-cloud-container1" class="row">
            <div class="card">
                <div class="card-header">
                    llm词云图
                </div>
                <div class="card-body">
                    <div id="llm-word-cloud"></div>
                </div>
            </div>
        </div>
    </div>
    <div v-else>
        <p>加载中...</p>
    </div>
</template>
<script>
import * as d3 from "d3";
import layoutCloud from "d3-cloud";

export default {
  name: "WordCloud",
  data() {
    return {
      errorMessage: null,
      stopwords2: new Set(
        "'d,ll,m,re,s,t,ve,ZT,ZZ,a,a's,able,about,above,abst,accordance,according,accordingly,across,act,actually,added,adj,adopted,affected,affecting,affects,after,afterwards,again,against,ah,ain't,all,allow,allows,almost,alone,along,already,also,although,always,am,among,amongst,an,and,announce,another,any,anybody,anyhow,anymore,anyone,anything,anyway,anyways,anywhere,apart,apparently,appear,appreciate,appropriate,approximately,are,area,areas,aren,aren't,arent,arise,around,as,aside,ask,asked,asking,asks,associated,at,auth,available,away,awfully,b,back,backed,backing,backs,be,became,because,become,becomes,becoming,been,before,beforehand,began,begin,beginning,beginnings,begins,behind,being,beings,believe,below,beside,besides,best,better,between,beyond,big,biol,both,brief,briefly,but,by,c,c'mon,c's,ca,came,can,can't,cannot,cant,case,cases,cause,causes,certain,certainly,changes,clear,clearly,co,com,come,comes,concerning,consequently,consider,considering,contain,containing,contains,corresponding,could,couldn't,couldnt,course,currently,d,date,definitely,describe,described,despite,did,didn't,differ,different,differently,discuss,do,does,doesn't,doing,don't,done,down,downed,downing,downs,downwards,due,during,e,each,early,ed,edu,effect,eg,eight,eighty,either,else,elsewhere,end,ended,ending,ends,enough,entirely,especially,et,et-al,etc,even,evenly,ever,every,everybody,everyone,everything,everywhere,ex,exactly,example,except,f,face,faces,fact,facts,far,felt,few,ff,fifth,find,finds,first,five,fix,followed,following,follows,for,former,formerly,forth,found,four,from,full,fully,further,furthered,furthering,furthermore,furthers,g,gave,general,generally,get,gets,getting,give,given,gives,giving,go,goes,going,gone,good,goods,got,gotten,great,greater,greatest,greetings,group,grouped,grouping,groups,h,had,hadn't,happens,hardly,has,hasn't,have,haven't,having,he,he's,hed,hello,help,hence,her,here,here's,hereafter,hereby,herein,heres,hereupon,hers,herself,hes,hi,hid,high,higher,highest,him,himself,his,hither,home,hopefully,how,howbeit,however,hundred,i,i'd,i'll,i'm,i've,id,ie,if,ignored,im,immediate,immediately,importance,important,in,inasmuch,inc,include,indeed,index,indicate,indicated,indicates,information,inner,insofar,instead,interest,interested,interesting,interests,into,invention,inward,is,isn't,it,it'd,it'll,it's,itd,its,itself,j,just,k,keep,keeps,kept,keys,kg,kind,km,knew,know,known,knows,l,large,largely,last,lately,later,latest,latter,latterly,least,less,lest,let,let's,lets,like,liked,likely,line,little,long,longer,longest,look,looking,looks,ltd,m,made,mainly,make,makes,making,man,many,may,maybe,me,mean,means,meantime,meanwhile,member,members,men,merely,mg,might,million,miss,ml,more,moreover,most,mostly,mr,mrs,much,mug,must,my,myself,n,n't,na,name,namely,nay,nd,near,nearly,necessarily,necessary,need,needed,needing,needs,neither,never,nevertheless,new,newer,newest,next,nine,ninety,no,nobody,non,none,nor,normally,nos,not,noted,nothing,novel,now,nowhere,number,numbers,o,obtain,obtained,obviously,of,off,often,oh,ok,okay,old,older,oldest,omitted,on,once,one,ones,only,onto,open,opened,opening,opens,or,ord,order,ordered,ordering,orders,other,others,otherwise,ought,our,ours,ourselves,out,outside,over,overall,owing,own,p,page,pages,part,parted,particular,particularly,parting,parts,past,per,perhaps,place,placed,places,please,plus,point,pointed,pointing,points,poorly,possible,possibly,potentially,pp,predominantly,present,presented,presenting,presents,presumably,previously,primarily,probably,problem,problems,promptly,proud,provides,put,puts,q,que,quickly,quite,qv,r,ran,rather,rd,re,readily,really,reasonably,recent,recently,ref,refs,regarding,regardless,regards,related,relatively,research,respectively,resulted,resulting,results,right,room,rooms,run,s,said,same,saw,say,saying,says,sec,second,secondly,seconds,section,see,seeing,seem,seemed,seeming,seems,seen,sees,self,selves,sensible,sent,serious,seriously,seven,several,shall,she,she'll,shed,shes,should,shouldn't,show,showed,showing,shown,show's,shows,side,sides,significant,significantly,similar,similarly,since,six,slightly,small,smaller,smallest,so,some,somebody,somehow,someone,somethan,something,sometime,sometimes,somewhat,somewhere,soon,sorry,specifically,specified,specify,specifying,state,states,still,stop,strongly,sub,substantially,successfully,such,sufficiently,suggest,sup,sure,t,t's,take,taken,taking,tell,tends,th,than,thank,thanks,thanx,that,that'll,that's,that've,thats,the,their,theirs,them,themselves,then,thence,there,there'll,there's,there've,thereafter,thereby,thered,therefore,therein,thereof,therere,theres,thereto,thereupon,these,they,they'd,they'll,they're,they've,theyd,theyre,thing,things,think,thinks,third,this,thorough,thoroughly,those,thou,though,thoughh,thought,thoughts,thousand,three,throug,through,throughout,thru,thus,til,tip,to,today,together,too,took,toward,towards,tried,tries,truly,try,trying,ts,turn,turned,turning,turns,twice,two,u,un,under,unfortunately,unless,unlike,unlikely,until,unto,up,upon,ups,us,use,used,useful,usefully,usefulness,uses,using,usually,uucp,v,value,various,very,via,viz,vol,vols,vs,w,want,wanted,wanting,wants,was,wasn't,way,ways,we,we'd,we'll,we're,we've,wed,welcome,well,wells,went,were,weren't,what,what'll,what's,whatever,whats,when,whence,whenever,where,where's,whereafter,whereas,whereby,wherein,wheres,whereupon,wherever,whether,which,while,whim,whither,who,who'll,who's,whod,whoever,whole,whom,whomever,whos,whose,why,widely,will,willing,wish,with,within,without,won't,wonder,words,work,worked,working,works,world,would,wouldn't,www,x,y,year,years,yes,yet,you,you'd,you'll,you're,you've,youd,young,younger,youngest,your,youre,yours,yourself,yourselves,z,zero,zt,zz,!,#,$,%,&,',(,),*,+,,-,.,/,:,;,<,=,>,?,@,[,],^,_,`,{,|,},~".split(",")      ),
    };
  },
  methods: {
    checkDependencies() {
      if (typeof d3 === "undefined") {
        this.errorMessage =
          "D3.js library failed to load. Please check your internet connection and try again.";
        return false;
      }
      if (typeof layoutCloud === "undefined") {
        this.errorMessage =
          "d3-cloud library failed to load. Please check your internet connection and try again.";
        return false;
      }
      return true;
    },
    processChunk(chunk, wordFrequency, partialWord) {
      let currentWord = partialWord;
      let inWord = false;

      for (let i = 0; i < chunk.length; i++) {
        const char = chunk[i];

        // 只处理英文字符

        if (/[a-zA-Z]/.test(char)) {
          if (!inWord) {
            inWord = true;
            currentWord = char;
          } else {
            currentWord += char;
          }
        } else {
          if (inWord) {
            const lowerCaseWord = currentWord.toLowerCase();
            if (!this.stopwords2.has(lowerCaseWord)) {
              wordFrequency.set(
                lowerCaseWord,
                (wordFrequency.get(lowerCaseWord) || 0) + 1
              );
            }
            inWord = false;
          }
        }
      }

      return inWord ? currentWord : "";
    },
    async processLargeFile(reader) {
      const wordFrequency = new Map();
      const decoder = new TextDecoder();
      let partialWord = "";

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        const chunk = decoder.decode(value, { stream: true });
        partialWord = this.processChunk(chunk, wordFrequency, partialWord);
      }

      // 处理最后一个剩余的单词片段

      if (partialWord) {
        const lowerCaseWord = partialWord.toLowerCase();
        wordFrequency.set(
          lowerCaseWord,
          (wordFrequency.get(lowerCaseWord) || 0) + 1
        );
      }

      return wordFrequency;
    },
    generateWordCloud(wordFrequency) {
      if (!wordFrequency || wordFrequency.size === 0) {
        console.error("No valid word data found.");
        this.errorMessage = "No valid words found to generate the word cloud.";
        return;
      }

      // 将词频数据转换为词云格式

      const wordData = Array.from(wordFrequency.entries())
        .filter(([word]) => !this.stopwords2.has(word) && /^[a-zA-Z]+$/.test(word)) // 过滤有效的英文单词

        .sort((a, b) => b[1] - a[1]) // 按频率排序

        .slice(0, 200) // 限制单词数量以提高性能

        .map(([word, count]) => ({ text: word, size: Math.sqrt(count) / 2 }));

      console.log("Word Cloud Data:", wordData); // 调试：将词云数据输出到控制台


      if (wordData.length === 0) {
        console.error("No valid word data for the word cloud.");
        this.errorMessage = "No valid words found to generate the word cloud.";
        return;
      }

      // 设置边框大小

      const width = 1200;
      const height = 600;

      // 确保 SVG 适合容器

      const svg = d3.select("#llm-word-cloud")
        .append("svg")
        .attr("viewBox", [0, 0, width, height])
        .attr("width", width)
        .attr("font-family", "sans-serif")
        .attr("text-anchor", "middle")
        .attr("style", "max-width: 100%; height: auto; height: intrinsic;");

      // 动态调整词云布局大小

      const layout = layoutCloud()
        .size([width - 50, height - 50])
        .words(wordData)
        .padding(1)
        .rotate(() => (~~(Math.random() * 6) - 3) * 30)
        .font("sans-serif")
        .fontSize(d => d.size)
        .on("end", draw);

      layout.start();

      function draw(words) {
        // 绘制单词

        svg.append("g")
          .attr("transform", `translate(${width /2},${height/2 })`)
          .selectAll("text")
          .data(words)
          .enter()
          .append("text")
          .style("font-size", d => d.size )
          .style("font-family", "sans-serif")
          .style("fill", () => `rgb(${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)}, ${Math.floor(Math.random() * 255)})`)
          .attr("text-anchor", "middle")
          .attr("transform", d => `translate(${d.x},${d.y}) rotate(${d.rotate})`)
          .text(d => d.text);
      }
    },
    async loadAndProcessFile() {
      if (!this.checkDependencies()) return;

      try {
        const response = await fetch("/public/llm.txt"); // 确保路径正确

        if (!response.ok) throw new Error("File loading failed");

        const reader = response.body.getReader();
        const wordFrequency = await this.processLargeFile(reader);

        console.log("Word frequency data:", wordFrequency);
        this.generateWordCloud(wordFrequency);
      } catch (error) {
        console.error("Error:", error);
        this.errorMessage = error.message || "An error occurred while generating the word cloud.";
      }
    },
  },
  mounted() {
    this.loadAndProcessFile();
  },
};
</script>

<style scoped>
#app {
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-align: center;
    color: #2c3e50;
}

@media (min-width: 1080px) {
    #app {
        width: 70%;
        margin: 0 auto;
    }
}

@media (max-width: 1080px) {
    #app {
        width: 100%;
    }
}

@font-face {
    font-family: 'main';
    src: url('fonts/LuoGuoChengMaoBiXiaoXianKaiJianTi/LuoGuoChengMaoBiXiaoXianKaiJianTi-2.ttf') format('truetype');
}

.allthing {
    font-family: 'main' !important;
}

.card-body {
    padding: 0; /* 移除外边距 */
}

.card-header {
    padding: 10px; /* 调整内边距 */
    font-size: 22px;
    text-align: center;
    font-weight: bold;
    border: 0;
    background: white;
}

#word-cloud-container1 {
    margin-top: 20px; /* 调整上边距 */
    margin-bottom: 20px; /* 调整下边距 */
}

#llm-word-cloud {
    width: 100%; /* 调整宽度为100% */
    height: 600px; /* 调整高度 */
    margin: 0 auto; /* 居中显示 */
}

:root {
    --thumb-position: 50%; /* 默认值 */
}

*,
:after,
:before {
    box-sizing: border-box;
}

html {
    font-family: sans-serif;
    line-height: 1.15;
}

[tabindex="-1"]:focus:not(:focus-visible) {
    outline: 0 !important;
}

p {
    margin-top: 0;
    margin-bottom: 1rem;
}

[type=button]:not(:disabled),
[type=reset]:not(:disabled),
[type=submit]:not(:disabled),
button:not(:disabled) {
    cursor: pointer;
}

::-webkit-file-upload-button {
    font: inherit;
    -webkit-appearance: button;
}

.row {
    display: flex;
    flex-wrap: wrap;
    margin-right: -15px;
    margin-left: -15px;
}

.card {
    position: relative;
    display: flex;
    flex-direction: column;
    min-width: 0;
    word-wrap: break-word;
    background-color: #fff;
    background-clip: border-box;
    border: 1px solid rgba(0, 0, 0, .125);
    border-radius: .25rem;
}

.card-body {
    flex: 1 1 auto;
    min-height: 1px;
    padding: 1.25rem;
}

.card-header {
    padding: .75rem 1.25rem;
    margin-bottom: 0;
    background-color: rgba(0, 0, 0, .03);
    border-bottom: 1px solid rgba(0, 0, 0, .125);
}

.card-header:first-child {
    border-radius: calc(.25rem - 1px) calc(.25rem - 1px) 0 0;
}

@media print {
    *,
    :after,
    :before {
        text-shadow: none !important;
        box-shadow: none !important;
    }

    p {
        orphans: 3;
        widows: 3;
    }

    @page {
        size: a3;
    }

    body {
        min-width: 992px !important;
    }
}

</style>